using System;
using CodeAngel.Domain.Framework;
using CodeAngel.Domain.Classes;
using System.Collections;

namespace CodeAngel.Domain
{
	public partial class EntityDef 
	{
		#region SetDefaultPropertyValues

        /// <summary>
        /// Sets the properties of a new entity to its default values.
        /// </summary>
		internal override void SetDefaultPropertyValues()
		{
            this.SetFriendlyIdAsync();
            this.EntityDefID = Guid.NewGuid();
            this.CanBeAggregateRoot = false;
            this.IsNameKeyword = false;
            
            if (this.ParentEntity is Domain)
                this.Domain = this.ParentEntity as Domain;
		}

		#endregion

        #region NameChanged

        /// <summary>
        /// Handles when the Name property changes value and updates related properties.
        /// </summary>
        partial void NameChanged(string previousValue, string newValue)
        {
            if (string.IsNullOrWhiteSpace(previousValue) && !string.IsNullOrWhiteSpace(newValue))
            {
                this.TableName = newValue;
                this.FriendlyName = this.GetSampleFriendlyName();
            }
        }

        /// <summary>
        /// Gets the default setting for the FriendlyName property based on the current Name.
        /// </summary>
        private string GetSampleFriendlyName()
        {
            ArrayList Words = new ArrayList();
            System.Text.StringBuilder WordBuilder = new System.Text.StringBuilder();
            char PreviousChar = ' ';
            char ThisChar = '\0';
            foreach (char ThisChar_loopVariable in this.Name)
            {
                ThisChar = ThisChar_loopVariable;
                if (char.IsUpper(ThisChar) && !char.IsUpper(PreviousChar))
                {
                    if (WordBuilder != null) Words.Add(WordBuilder.ToString());
                    WordBuilder = new System.Text.StringBuilder();
                }
                WordBuilder.Append(ThisChar);
                PreviousChar = ThisChar;
            }
            Words.Add(WordBuilder.ToString());

            //Put the words together with a space between each one.
            string ThisWord = null;
            string ThisString = null;
            foreach (string ThisWord_loopVariable in Words)
            {
                ThisWord = ThisWord_loopVariable;
                ThisString += ThisWord + " ";
            }

            return ThisString.Trim();

        }

        #endregion

        #region CanBeAggregateRootChanged

        partial void CanBeAggregateRootChanged(bool previousValue, bool newValue)
        {
            if (newValue == true && this.FilterRelation == null)
               this.CreateFilterRelation();
        }

        #endregion

        #region CreateFilterRelation

        public void CreateFilterRelation()
        {
            this.FilterRelation = new FilterRelation(this.Aggregate, this);
            this.FilterRelation.InitializeAllProperties();
            this.FilterRelation.SetDefaultPropertyValues();
        }

        #endregion

        #region StoredProcedureNames

        /// <summary>
        /// Helps idenitfy the stored procedure names for this EntityDef.
        /// </summary>
        public StoredProcedureNames StoredProcedureNames
        {
            get
            {
                if (this._StoredProcedureNames == null) this._StoredProcedureNames = new StoredProcedureNames(this);
                return this._StoredProcedureNames;
            }
        }
        private StoredProcedureNames _StoredProcedureNames;

        #endregion

        #region EntitiesDirectoryPath

        /// <summary>
        /// Gets the path to the directory where this entity should be written to.
        /// </summary>
        public string EntitiesDirectoryPath
        {
            get { return System.IO.Path.Combine(this.Domain.EntitiesPath, this.SubFolder); }
        }

        #endregion

        #region Namespace

        /// <summary>
        /// Gets the namespace this entity should be part of.
        /// </summary>
        public string Namespace
        {
            get { return this.Domain.Namespace + ".Domain"; }
        }

        #endregion

        #region CriteriaNamespace

        /// <summary>
        /// Identifies the namespace where the criteria for this entity is defined.
        /// </summary>
        public string CriteriaNamespace
        {
            get { return this.Domain.Namespace + ".Domain." + this.Name + "Criteria"; }
        }

        #endregion

        #region IsUserEntityDef

        /// <summary>
        /// To identify if this entity definition is the very special User Entity.
        /// </summary>
        public bool IsUserEntityDef
        {
            get { return this.Name == "User"; }
        }

        #endregion

        #region IsRoleEntityDef

        /// <summary>
        /// To identify if this entity definition is the very special Role Entity.
        /// </summary>
        public bool IsRoleEntityDef
        {
            get { return this.Name == "Role"; }
        }

        #endregion

        #region ClassNames

        /// <summary>
        /// Helps identify how the class names (for those classes related to this EntityEefinition)
        /// should be typed throughout the code generation process under a variety of contexts.
        /// </summary>
        public ClassNames ClassNames
        {
            get
            {
                if (this._classNames == null) this._classNames = new ClassNames(this);
                return this._classNames;
            }
        }
        private ClassNames _classNames;

        #endregion

        #region FileNames

        /// <summary>
        /// Helps idenitfy the file names for this EntityDef.
        /// </summary>
        public FileNames FileNames
        {
            get
            {
                if (this._fileNames == null) this._fileNames = new FileNames(this);
                return this._fileNames;
            }
        }
        private FileNames _fileNames;


        #endregion

        #region ProcedureNames

        /// <summary>
        /// Helps idenitfy the Procedure names for this EntityDef.
        /// </summary>
        internal ProcedureNames ProcedureNames
        {
            get
            {
                if (this._procedureNames == null) this._procedureNames = new ProcedureNames(this);
                return this._procedureNames;
            }
        }
        private ProcedureNames _procedureNames;

        #endregion

        #region VariableNames

        /// <summary>
        /// Helps idenitfy the Variable names for this EntityDef.
        /// </summary>
        internal VariableNames VariableNames
        {
            get
            {
                if (this._VariableNames == null) this._VariableNames = new VariableNames(this);
                return this._VariableNames;
            }
        }
        private VariableNames _VariableNames;

        #endregion

        #region PrimaryKey

        /// <summary>
        /// Gets the primary key PropertyDef for this EntityDef.
        /// </summary>
        public PropertyDef PrimaryKeyPropertyDef
        {
            get
            {
                if ((this._primaryKeyPropertyDef == null))
                    this._primaryKeyPropertyDef = this.PropertyDefs.FindPrimaryKey();
                return this._primaryKeyPropertyDef;
            }
        }
        private PropertyDef _primaryKeyPropertyDef;

        #endregion

        #region FriendlyIdPropertyDef

        /// <summary>
        /// To identify the Friendly ID property definition of this EntityDefintion, if there is
        /// one.  If there isn't, then nothing is returned.
        /// </summary>
        public PropertyDef FriendlyIdPropertyDef
        {
            get
            {
                foreach (var ThisPD in this.PropertyDefs)
                    if (ThisPD.IsFriendlyID) return ThisPD;
                return null;
            }
        }

        /// <summary>
        /// Identifies if this EntityDefintion has the unique property defintion, FriendlyID.
        /// </summary>
        public bool HasFriendlyIDPropertyDef
        {
            get { return this.FriendlyIdPropertyDef.IsSomething(); }
        }

        #endregion

        #region AddPropertyDef

        public void AddPropertyDef_PrimaryKey()
        {
            if (this.PropertyDefs.Find("PrimaryKey").IsSomething()) return;

            var ThisPD = this.PropertyDefs.AddNew();
            ThisPD.Name = this.Name + "ID";
            ThisPD.FriendlyName = this.Name + "ID";
            ThisPD.DatabaseColumnName = this.Name + "ID";
            ThisPD.Purpose = "To uniquely identify this entity.";
            ThisPD.DataType = "Guid";
            ThisPD.SqlDataType = "uniqueidentifier";
            ThisPD.SqlDefaultValue = "newid()";
            ThisPD.IsNullable = false;
            ThisPD.IsPrimaryKey = true;

        }

        public void AddPropertyDef_FriendlyID()
        {
            if (this.PropertyDefs.Find("FriendlyID").IsSomething()) return;

            var ThisPD = this.PropertyDefs.AddNew();
            ThisPD.Name = "FriendlyID";
            ThisPD.FriendlyName = "ID";
            ThisPD.DatabaseColumnName = "FriendlyID";
            ThisPD.Purpose = "To uniquely identify this entity with a data type that is easier to work with than the ID/primary key (i.e. GUID) property.";
            ThisPD.DataType = "string";
            ThisPD.SqlDataType = "varcharmax";
            //ThisPD.SqlDataLength = 8;
            ThisPD.IsNullable = false;
            //ThisPD.MustBeUnique = true;
            ThisPD.IsFriendlyID = true;
        }

        public void AddPropertyDef_Name()
        {
            if (this.PropertyDefs.Find("Name").IsSomething()) return;

            var ThisPD = this.PropertyDefs.AddNew();
            ThisPD.Name = "Name";
            ThisPD.FriendlyName = "Name";
            ThisPD.DatabaseColumnName = "Name";
            ThisPD.Purpose = "To identify the name of this " + this.Name + ".";
            ThisPD.DataType = "string";
            ThisPD.SqlDataType = "varcharmax";
            //ThisPD.SqlDataLength = 50;
            ThisPD.IsNullable = false;
            //ThisPD.MustBeUnique = true;
        }

        public void AddPropertyDef_Notes()
        {
            if (this.PropertyDefs.Find("Notes").IsSomething()) return;

            var ThisPD = this.PropertyDefs.AddNew();
            ThisPD.Name = "Notes";
            ThisPD.FriendlyName = "Notes";
            ThisPD.DatabaseColumnName = "Notes";
            ThisPD.Purpose = "To enable associating any miscellaneous data with this entity.";
            ThisPD.DataType = "string";
            ThisPD.SqlDataType = "varcharmax";
            ThisPD.IsNullable = true;
        }

        #endregion

        #region ToString
        
        public override string ToString()
        {
            return this.Name;
        }

        #endregion
    }

    #region EntityDefNames

    #region ClassNames

    /// <summary>
    /// Identifies the class names for a particuar EntityDef and how they
    /// should be typed throughout the code generation process under a variety of contexts.
    /// </summary>
    public class ClassNames : EntityDefNames
    {
        public ClassNames(EntityDef thisED)
            : base(thisED) { }

        /// <summary>
        /// Gets the Class name for the identified EntityDef, properly formatted if
        /// the name is a keyword.
        /// </summary>
        public string ClassName()
        {
            if (this.EntityDef.IsNameKeyword)
                //return "[" + this.EntityDef.Name + "]";
                return "@" + this.EntityDef.Name;
            else
                return this.EntityDef.Name;
        }

        /// <summary>
        /// Gets the name of the DTO class for this EntityDef.
        /// </summary>
        public string Dto()
        {
            return this.EntityDef.Name + "Dto";
        }

        /// <summary>
        /// Gets the name of the Collection class for this EntityDef.
        /// </summary>
        public string Collection()
        {
            return this.EntityDef.Name + "Collection";
        }

        /// <summary>
        /// Gets the name of the Filter class for this EntityDef.
        /// </summary>
        public string Filter()
        {
            return this.EntityDef.Name + "Filter";
        }

        /// <summary>
        /// Gets the name of the DeleteAggregate class for this EntityDef.
        /// </summary>
        public string DeleteAggregate()
        {
            return this.EntityDef.Name + "DeleteAggregate";
        }

    }

    #endregion

    #region ProcedureNames

    /// <summary>
    /// To help idenitfy procedure names for a particuar EntityDef.
    /// </summary>
    public class ProcedureNames : EntityDefNames
    {
        public ProcedureNames(EntityDef thisED)
            : base(thisED)
        {
        }

        #region CreateDtosFromDataReader

        /// <summary>
        /// Identifies the name of the procedure used to create the DTOs by reading the data
        /// from the database through a SqlDataReader object.
        /// </summary>
        public string CreateDtosFromDataReader()
        {
            return "Create" + this.EntityDef.ClassNames.Dto() + "s";
        }

        #endregion



    }

    #endregion

    #region VariableNames

    /// <summary>
    /// To help idenitfy variable names for a particuar EntityDef.
    /// </summary>
    public class VariableNames : EntityDefNames
    {
        public VariableNames(EntityDef thisED)
            : base(thisED)
        {
        }

        #region DTO

        /// <summary>
        /// Identifies the name of this entity's dto when used as a variable
        /// </summary>
        public string CreateDtosFromDataReader()
        {
            return string.Format("This{0}", this.EntityDef.ClassNames.Dto());
        }

        #endregion



    }
    
    #endregion

    #region FileNames

    /// <summary>
    /// Idenitfies the file names for a particuar EntityDef.
    /// </summary>
    public class FileNames : EntityDefNames
    {
        public FileNames(EntityDef thisED)
            : base(thisED) { }

        public string Class()
        {
            return this.EntityDef.Name + ".cs";
        }
        public string ClassDesigner()
        {
            return this.EntityDef.Name + ".Designer.cs";
        }

        public string Collection()
        {
            return this.EntityDef.Name + ".Collection.cs";
        }
        public string CollectionDesigner()
        {
            return this.EntityDef.Name + ".Collection.Designer.cs";
        }

        public string Dto()
        {
            return this.EntityDef.Name + ".Dto.cs";
        }
        public string DtoDesigner()
        {
            return this.EntityDef.Name + ".Dto.Designer.cs";
        }

        public string Filter()
        {
            return this.EntityDef.Name + ".Filter.cs";
        }
        public string FilterDesigner()
        {
            return this.EntityDef.Name + ".Filter.Designer.cs";
        }

        public string Aggregate(AggregateDef thisAggregate)
        {
            return this.EntityDef.Name + "Aggregate." + thisAggregate.Name + ".cs";
        }
        public string AggregateDesigner(AggregateDef thisAggregate)
        {
            return this.EntityDef.Name + "Aggregate." + thisAggregate.Name + ".Designer.cs";
        }

        public string DeleteAggregate()
        {
            return this.EntityDef.Name + "DeleteAggregate.cs";
        }

        public string Criteria()
        {
            return this.EntityDef.Name + ".Criteria.cs";
        }
    }

    #endregion

    #region StoredProcedureNames

    /// <summary>
    /// To help idenitfy the stored procedure names for a particuar EntityDef.
    /// </summary>
    public class StoredProcedureNames : EntityDefNames
    {

        public StoredProcedureNames(EntityDef thisED)
            : base(thisED) { }

        public string Get()
        {
            return "Get_" + this.EntityDef.TableName;
        }

        public string Insert()
        {
            return "Insert_" + this.EntityDef.TableName;
        }

        public string Update()
        {
            return "Update_" + this.EntityDef.TableName;
        }

        public string Delete()
        {
            return "Delete_" + this.EntityDef.TableName;
        }
    }

    #endregion

    #region EntityDefNames

    /// <summary>
    /// Helps track the EntityDef for when creating names for soucre code generation.
    /// </summary>
    public abstract class EntityDefNames
    {
        public EntityDefNames(EntityDef thisED)
        {
            this.EntityDef = thisED;
        }
        protected EntityDef EntityDef { get; set; }
    }

    #endregion

    #endregion


    /// <summary>
    /// A set of extension methods to help identify how various names for EntityDef
    /// and PropertyDef objects should be typed throughout the code generation process.
    /// </summary>
    public static class Names
    {
        #region GetClassName
        
        ///// <summary>
        ///// Gets the Class name for the identified EntityDef, properly formatted if
        ///// the name is a keyword.
        ///// </summary>
        //public static string GetClassName(this EntityDef thisED)
        //{
        //    if (thisED.IsNameKeyword)
        //        return "[" + thisED.Name + "]";
        //    else
        //        return thisED.Name;
        //}

        #endregion

        #region GetFieldName

        ///// <summary>
        ///// 
        ///// </summary>
        //public static string GetFieldName(this PropertyDef thisPD)
        //{
        //    if (thisPD.IsEntityDef)
        //        return thisPD.Name + "ID";
        //    else
        //        return thisPD.Name;
        //}

        #endregion
    }

    /// <summary>
    /// To help identify how the class names (for those classes related to an entity definition)
    /// should be typed throughout the code generation process under a variety of contexts.
    /// </summary>
    public class ClassNamesOld
    {
        #region Constructors

        public ClassNamesOld(EntityDef thisED)
        {
            this._ed = thisED;
        }
        private EntityDef _ed;

        #endregion

        ///
        //NAME: Class
        //PURPOSE: The class name for the entity definition.  If the name of the class is a keyword
        //and we are not asked to qualify it with a namespace, then the returned name will be escaped.
        ///
        public string Class()
        {
            return this.Class(NamespaceQualification.None);
        }
        public string Class(NamespaceQualification qualification)
        {
            if (qualification == NamespaceQualification.None)
            {
                if (this._ed.IsNameKeyword)
                {
                    return "[" + this._ed.Name + "]";
                }
                else
                {
                    return this._ed.Name;
                }
            }
            else
            {
                return this.GetNamespace(qualification) + this._ed.Name;
            }
        }

        ///
        //NAME: Collection
        ///
        public string Collection()
        {
            return this.Collection(NamespaceQualification.None);
        }
        public string Collection(NamespaceQualification qualification)
        {
            return this.GetNamespace(qualification) + this._ed.Name + "Collection";
        }

        ///
        //NAME: ListItem
        ///
        public string ListItem()
        {
            return this.ListItem(NamespaceQualification.None);
        }
        public string ListItem(NamespaceQualification qualification)
        {
            return this.GetNamespace(qualification) + this._ed.Name + "ListItem";
        }

        ///
        //NAME: Filter
        ///
        public string Filter()
        {
            return this.Filter(NamespaceQualification.None);
        }
        public string Filter(NamespaceQualification qualification)
        {
            return this.GetNamespace(qualification) + this._ed.Name + "Filter";
        }

        ///
        //NAME: Dto
        ///
        public string Dto()
        {
            return this.Dto(NamespaceQualification.None);
        }
        public string Dto(NamespaceQualification qualification)
        {
            return this.GetNamespace(qualification) + this._ed.Name + "Dto";
        }

        ///
        //NAME: Row
        ///
        public string Row()
        {
            return this.Row(NamespaceQualification.None);
        }
        public string Row(NamespaceQualification qualification)
        {
            return this.GetNamespace(qualification) + this._ed.Name + "Row";
        }

        ///
        //NAME: AggregateBase
        ///
        public string AggregateBase()
        {
            return this.AggregateBase(NamespaceQualification.None);
        }
        public string AggregateBase(NamespaceQualification qualification)
        {
            //Return Me.GetNamespace(qualification) & Me.mED.AggregateNamespace(False) & "." & Me.mED.Name & "AggregateBase"
            return this.GetNamespace(qualification) + this._ed.Name + "AggregateBase";
        }

        ///
        //NAME: Aggregate
        ///
        public string Aggregate(AggregateDef thisAggregate)
        {
            return this.Aggregate(thisAggregate, NamespaceQualification.None);
        }
        public string Aggregate(AggregateDef thisAggregate, NamespaceQualification qualification)
        {
            return this.GetNamespace(qualification) + thisAggregate.Name;
        }


        ///
        //NAME: Table
        ///
        public string Table()
        {
            return this.Table(NamespaceQualification.None);
        }
        public string Table(NamespaceQualification qualification)
        {
            return this.GetNamespace(qualification) + this._ed.Name + "Table";
        }

        ///
        //NAME: GetNamespace
        //PURPOSE: To return the namespace string based on the identified qualification level.
        ///
        private string GetNamespace(NamespaceQualification qualification)
        {
            switch (qualification)
            {
                case NamespaceQualification.Domain:

                    return this._ed.Domain.Namespace + ".Domain.";
                default:

                    return "";
            }
        }

    }



    /// <summary>
    /// Identifies how a namespace should be qualified.
    /// </summary>
    public enum NamespaceQualification
    {
        None,
        Domain
    }




}

